Implement block-cursor for overwrite mode. (#80378)
authorYevgen Muntyan <muntyan@tamu.edu>
Tue, 12 Jun 2007 05:32:57 +0000 (05:32 +0000)
committerBehdad Esfahbod <behdad@src.gnome.org>
Tue, 12 Jun 2007 05:32:57 +0000 (05:32 +0000)
2007-06-12  Yevgen Muntyan  <muntyan@tamu.edu>

        * gtk/gtkentry.c (gtk_entry_expose), (gtk_entry_toggle_overwrite),
        (gtk_entry_draw_cursor):
        * gtk/gtkstyle.c (_gtk_widget_get_cursor_gc),
        (_gtk_widget_get_cursor_color):
        * gtk/gtkstyle.h:
        * gtk/gtktextdisplay.c (gtk_text_renderer_prepare_run),
        (gtk_text_renderer_draw_shape), (text_renderer_set_state),
        (render_para):
        * gtk/gtktextlayout.c (gtk_text_layout_set_overwrite_mode),
        (gtk_text_layout_invalidate_cache), (get_block_cursor),
        (add_cursor), (gtk_text_layout_get_line_display),
        (_gtk_text_layout_get_block_cursor):
        * gtk/gtktextlayout.h:
        * gtk/gtktextutil.c (layout_get_char_width),
        (_gtk_text_util_get_block_cursor_location):
        * gtk/gtktextutil.h:
        * gtk/gtktextview.c (gtk_text_view_set_editable),
        (gtk_text_view_toggle_overwrite), (gtk_text_view_set_overwrite),
        (gtk_text_view_ensure_layout), (text_window_invalidate_cursors):
        Implement block-cursor for overwrite mode.  (#80378)

svn path=/trunk/; revision=18108

ChangeLog
gtk/gtkentry.c
gtk/gtkstyle.c
gtk/gtkstyle.h
gtk/gtktextdisplay.c
gtk/gtktextlayout.c
gtk/gtktextlayout.h
gtk/gtktextutil.c
gtk/gtktextutil.h
gtk/gtktextview.c

index c359c0681a9a4e92f09a693ba5feba93266ada79..c20d322d04c0f458e181fbd04a33355f13e48b90 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,26 @@
+2007-06-12  Yevgen Muntyan  <muntyan@tamu.edu>
+
+       * gtk/gtkentry.c (gtk_entry_expose), (gtk_entry_toggle_overwrite),
+       (gtk_entry_draw_cursor):
+       * gtk/gtkstyle.c (_gtk_widget_get_cursor_gc),
+       (_gtk_widget_get_cursor_color):
+       * gtk/gtkstyle.h:
+       * gtk/gtktextdisplay.c (gtk_text_renderer_prepare_run),
+       (gtk_text_renderer_draw_shape), (text_renderer_set_state),
+       (render_para):
+       * gtk/gtktextlayout.c (gtk_text_layout_set_overwrite_mode),
+       (gtk_text_layout_invalidate_cache), (get_block_cursor),
+       (add_cursor), (gtk_text_layout_get_line_display),
+       (_gtk_text_layout_get_block_cursor):
+       * gtk/gtktextlayout.h:
+       * gtk/gtktextutil.c (layout_get_char_width),
+       (_gtk_text_util_get_block_cursor_location):
+       * gtk/gtktextutil.h:
+       * gtk/gtktextview.c (gtk_text_view_set_editable),
+       (gtk_text_view_toggle_overwrite), (gtk_text_view_set_overwrite),
+       (gtk_text_view_ensure_layout), (text_window_invalidate_cursors):
+       Implement block-cursor for overwrite mode.  (#80378)
+
 2007-06-11  Matthias Clasen  <mclasen@redhat.com>
 
        * configure.in: Bump GLib requirement to 2.13.3 (#446616, Behdad
index 7d4b494b3acba744fd91e256478fa83658302271..707aa90db7e6a37ba03444903b2d688cf88da9b6 100644 (file)
@@ -1580,16 +1580,16 @@ gtk_entry_expose (GtkWidget      *widget,
                          GTK_WIDGET_STATE(widget), GTK_SHADOW_NONE,
                          &event->area, widget, "entry_bg",
                          0, 0, area_width, area_height);
-      
-      if ((entry->visible || entry->invisible_char != 0) &&
-         GTK_WIDGET_HAS_FOCUS (widget) &&
-         entry->selection_bound == entry->current_pos && entry->cursor_visible)
-       gtk_entry_draw_cursor (GTK_ENTRY (widget), CURSOR_STANDARD);
 
       if (entry->dnd_position != -1)
        gtk_entry_draw_cursor (GTK_ENTRY (widget), CURSOR_DND);
       
       gtk_entry_draw_text (GTK_ENTRY (widget));
+
+      if ((entry->visible || entry->invisible_char != 0) &&
+         GTK_WIDGET_HAS_FOCUS (widget) &&
+         entry->selection_bound == entry->current_pos && entry->cursor_visible)
+       gtk_entry_draw_cursor (GTK_ENTRY (widget), CURSOR_STANDARD);
     }
 
   return FALSE;
@@ -2977,6 +2977,8 @@ static void
 gtk_entry_toggle_overwrite (GtkEntry *entry)
 {
   entry->overwrite_mode = !entry->overwrite_mode;
+  gtk_entry_pend_cursor_blink (entry);
+  gtk_widget_queue_draw (GTK_WIDGET (entry));
 }
 
 static void
@@ -3593,64 +3595,111 @@ gtk_entry_draw_cursor (GtkEntry  *entry,
       GtkWidget *widget = GTK_WIDGET (entry);
       GdkRectangle cursor_location;
       gboolean split_cursor;
-
+      PangoRectangle cursor_rect;
       GtkBorder inner_border;
       gint xoffset;
-      gint strong_x, weak_x;
       gint text_area_height;
-      PangoDirection dir1 = PANGO_DIRECTION_NEUTRAL;
-      PangoDirection dir2 = PANGO_DIRECTION_NEUTRAL;
-      gint x1 = 0;
-      gint x2 = 0;
+      gint cursor_index;
+      gboolean block;
+      gboolean block_at_line_end;
 
       _gtk_entry_effective_inner_border (entry, &inner_border);
 
       xoffset = inner_border.left - entry->scroll_offset;
 
       gdk_drawable_get_size (entry->text_area, NULL, &text_area_height);
-      
-      gtk_entry_get_cursor_locations (entry, type, &strong_x, &weak_x);
 
-      g_object_get (gtk_widget_get_settings (widget),
-                   "gtk-split-cursor", &split_cursor,
-                   NULL);
+      cursor_index = g_utf8_offset_to_pointer (entry->text, entry->current_pos + entry->preedit_cursor) - entry->text;
+      if (!entry->overwrite_mode)
+        block = FALSE;
+      else
+        block = _gtk_text_util_get_block_cursor_location (gtk_entry_ensure_layout (entry, TRUE),
+                                                          cursor_index, &cursor_rect, &block_at_line_end);
+
+      if (!block)
+        {
+          gint strong_x, weak_x;
+          PangoDirection dir1 = PANGO_DIRECTION_NEUTRAL;
+          PangoDirection dir2 = PANGO_DIRECTION_NEUTRAL;
+          gint x1 = 0;
+          gint x2 = 0;
+
+          gtk_entry_get_cursor_locations (entry, type, &strong_x, &weak_x);
+
+          g_object_get (gtk_widget_get_settings (widget),
+                        "gtk-split-cursor", &split_cursor,
+                        NULL);
 
-      dir1 = entry->resolved_dir;
+          dir1 = entry->resolved_dir;
       
-      if (split_cursor)
-       {
-         x1 = strong_x;
+          if (split_cursor)
+            {
+              x1 = strong_x;
 
-         if (weak_x != strong_x)
-           {
-             dir2 = (entry->resolved_dir == PANGO_DIRECTION_LTR) ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
-             x2 = weak_x;
-           }
-       }
-      else
-       {
-         if (keymap_direction == entry->resolved_dir)
-           x1 = strong_x;
-         else
-           x1 = weak_x;
-       }
+              if (weak_x != strong_x)
+                {
+                  dir2 = (entry->resolved_dir == PANGO_DIRECTION_LTR) ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
+                  x2 = weak_x;
+                }
+            }
+          else
+            {
+              if (keymap_direction == entry->resolved_dir)
+                x1 = strong_x;
+              else
+                x1 = weak_x;
+            }
 
-      cursor_location.x = xoffset + x1;
-      cursor_location.y = inner_border.top;
-      cursor_location.width = 0;
-      cursor_location.height = text_area_height - inner_border.top - inner_border.bottom;
+          cursor_location.x = xoffset + x1;
+          cursor_location.y = inner_border.top;
+          cursor_location.width = 0;
+          cursor_location.height = text_area_height - inner_border.top - inner_border.bottom;
 
-      draw_insertion_cursor (entry,
-                            &cursor_location, TRUE, dir1,
-                            dir2 != PANGO_DIRECTION_NEUTRAL);
+          draw_insertion_cursor (entry,
+                                 &cursor_location, TRUE, dir1,
+                                 dir2 != PANGO_DIRECTION_NEUTRAL);
       
-      if (dir2 != PANGO_DIRECTION_NEUTRAL)
-       {
-         cursor_location.x = xoffset + x2;
-         draw_insertion_cursor (entry,
-                                &cursor_location, FALSE, dir2,
-                                TRUE);
-       }
+          if (dir2 != PANGO_DIRECTION_NEUTRAL)
+            {
+              cursor_location.x = xoffset + x2;
+              draw_insertion_cursor (entry,
+                                     &cursor_location, FALSE, dir2,
+                                     TRUE);
+            }
+        }
+      else /* overwrite_mode */
+        {
+          PangoLayout *layout = gtk_entry_ensure_layout (entry, TRUE);
+          GdkColor cursor_color;
+          GdkRectangle rect;
+          cairo_t *cr;
+          gint x, y;
+
+          get_layout_position (entry, &x, &y);
+
+          rect.x = PANGO_PIXELS (cursor_rect.x) + x;
+          rect.y = PANGO_PIXELS (cursor_rect.y) + y;
+          rect.width = PANGO_PIXELS (cursor_rect.width);
+          rect.height = PANGO_PIXELS (cursor_rect.height);
+
+          cr = gdk_cairo_create (entry->text_area);
+
+          _gtk_widget_get_cursor_color (widget, &cursor_color);
+          gdk_cairo_set_source_color (cr, &cursor_color);
+          gdk_cairo_rectangle (cr, &rect);
+          cairo_fill (cr);
+
+          if (!block_at_line_end)
+            {
+              gdk_cairo_rectangle (cr, &rect);
+              cairo_clip (cr);
+              cairo_move_to (cr, x, y);
+              gdk_cairo_set_source_color (cr, &widget->style->base[widget->state]);
+              pango_cairo_show_layout (cr, layout);
+            }
+
+          cairo_destroy (cr);
+        }
     }
 }
 
index 763cc370861cc39823ae3918d581372584e32cd2..7854778991d3197b4bccae3364e7b1b9421caff3 100644 (file)
@@ -6665,6 +6665,34 @@ get_insertion_cursor_gc (GtkWidget *widget,
     }
 }
 
+GdkGC *
+_gtk_widget_get_cursor_gc (GtkWidget *widget)
+{
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+  g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL);
+  return get_insertion_cursor_gc (widget, TRUE);
+}
+
+void
+_gtk_widget_get_cursor_color (GtkWidget *widget,
+                             GdkColor  *color)
+{
+  GdkColor *style_color;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+  g_return_if_fail (color != NULL);
+
+  gtk_widget_style_get (widget, "cursor-color", &style_color, NULL);
+
+  if (style_color)
+    {
+      *color = *style_color;
+      gdk_color_free (style_color);
+    }
+  else
+    *color = widget->style->text[GTK_STATE_NORMAL];
+}
+
 static void
 draw_insertion_cursor (GtkWidget        *widget,
                       GdkDrawable      *drawable,
index 0e758f1eac5b13974d9a06f3976a3682937489e3..bed56e0c2fb2dfc174c598a056ff344d1169963f 100644 (file)
@@ -899,6 +899,9 @@ void   gtk_draw_insertion_cursor (GtkWidget        *widget,
                                  gboolean          is_primary,
                                  GtkTextDirection  direction,
                                  gboolean          draw_arrow);
+GdkGC *_gtk_widget_get_cursor_gc    (GtkWidget *widget);
+void   _gtk_widget_get_cursor_color (GtkWidget *widget,
+                                    GdkColor  *color);
 
 G_END_DECLS
 
index d782efdc5c012dfc9cc88209279615e6ab29635e..e9da20e500ca29e46b29340432ad400abd9eb97b 100644 (file)
 typedef struct _GtkTextRenderer      GtkTextRenderer;
 typedef struct _GtkTextRendererClass GtkTextRendererClass;
 
+enum {
+  NORMAL,
+  SELECTED,
+  CURSOR
+};
+
 struct _GtkTextRenderer
 {
   GdkPangoRenderer parent_instance;
@@ -106,7 +112,7 @@ struct _GtkTextRenderer
   GdkColor *error_color;       /* Error underline color for this widget */
   GList *widgets;              /* widgets encountered when drawing */
   
-  gboolean selected;
+  int state;
 };
 
 struct _GtkTextRendererClass
@@ -186,21 +192,23 @@ gtk_text_renderer_prepare_run (PangoRenderer  *renderer,
 
   appearance = get_item_appearance (run->item);
   g_assert (appearance != NULL);
-  
-  if (appearance->draw_bg && !text_renderer->selected)
+
+  if (appearance->draw_bg && text_renderer->state == NORMAL)
     bg_color = &appearance->bg_color;
   else
     bg_color = NULL;
   
   text_renderer_set_gdk_color (text_renderer, PANGO_RENDER_PART_BACKGROUND, bg_color);
 
-  if (text_renderer->selected)
+  if (text_renderer->state == SELECTED)
     {
       if (GTK_WIDGET_HAS_FOCUS (text_renderer->widget))
        fg_color = &text_renderer->widget->style->text[GTK_STATE_SELECTED];
       else
        fg_color = &text_renderer->widget->style->text[GTK_STATE_ACTIVE];
     }
+  else if (text_renderer->state == CURSOR && GTK_WIDGET_HAS_FOCUS (text_renderer->widget))
+    fg_color = &text_renderer->widget->style->base[GTK_STATE_NORMAL];
   else
     fg_color = &appearance->fg_color;
 
@@ -249,13 +257,15 @@ gtk_text_renderer_draw_shape (PangoRenderer   *renderer,
   GtkTextRenderer *text_renderer = GTK_TEXT_RENDERER (renderer);
   GdkGC *fg_gc;
 
-  if (text_renderer->selected)
+  if (text_renderer->state == SELECTED)
     {
       if (GTK_WIDGET_HAS_FOCUS (text_renderer->widget))
        fg_gc = text_renderer->widget->style->text_gc[GTK_STATE_SELECTED];
       else
        fg_gc = text_renderer->widget->style->text_gc[GTK_STATE_SELECTED];
     }
+  else if (text_renderer->state == CURSOR && GTK_WIDGET_HAS_FOCUS (text_renderer->widget))
+    fg_gc = text_renderer->widget->style->base_gc[GTK_STATE_NORMAL];
   else
     fg_gc = text_renderer->widget->style->text_gc[GTK_STATE_NORMAL];
   
@@ -358,10 +368,10 @@ _gtk_text_renderer_class_init (GtkTextRendererClass *klass)
 }
 
 static void
-text_renderer_set_selected (GtkTextRenderer *text_renderer,
-                           gboolean         selected)
+text_renderer_set_state (GtkTextRenderer *text_renderer,
+                        int              state)
 {
-  text_renderer->selected = selected;
+  text_renderer->state = state;
 }
 
 static void
@@ -486,6 +496,7 @@ render_para (GtkTextRenderer    *text_renderer,
       int first_y, last_y;
       PangoRectangle line_rect;
       int baseline;
+      gboolean at_last_line;
       
       pango_layout_iter_get_line_extents (iter, NULL, &line_rect);
       baseline = pango_layout_iter_get_baseline (iter);
@@ -508,8 +519,9 @@ render_para (GtkTextRenderer    *text_renderer,
           selection_y -= line_display->top_margin;
           selection_height += line_display->top_margin;
         }
-      
-      if (pango_layout_iter_at_last_line (iter))
+
+      at_last_line = pango_layout_iter_at_last_line (iter);
+      if (at_last_line)
         selection_height += line_display->bottom_margin;
       
       first = FALSE;
@@ -525,7 +537,7 @@ render_para (GtkTextRenderer    *text_renderer,
                               screen_width,
                               selection_height);
 
-         text_renderer_set_selected (text_renderer, TRUE);
+         text_renderer_set_state (text_renderer, SELECTED);
          pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer),
                                           line, 
                                           PANGO_SCALE * x + line_rect.x,
@@ -552,7 +564,7 @@ render_para (GtkTextRenderer    *text_renderer,
               g_object_unref (bg_gc);
             }
         
-         text_renderer_set_selected (text_renderer, FALSE);
+         text_renderer_set_state (text_renderer, NORMAL);
          pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer),
                                           line, 
                                           PANGO_SCALE * x + line_rect.x,
@@ -589,7 +601,7 @@ render_para (GtkTextRenderer    *text_renderer,
                                   PANGO_PIXELS (line_rect.width),
                                   selection_height);
 
-             text_renderer_set_selected (text_renderer, TRUE);
+             text_renderer_set_state (text_renderer, SELECTED);
              pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer),
                                               line, 
                                               PANGO_SCALE * x + line_rect.x,
@@ -636,6 +648,56 @@ render_para (GtkTextRenderer    *text_renderer,
                                       selection_height);
                 }
             }
+         else if (line_display->has_block_cursor &&
+                  GTK_WIDGET_HAS_FOCUS (text_renderer->widget) &&
+                  byte_offset <= line_display->insert_index &&
+                  (line_display->insert_index < byte_offset + line->length ||
+                   (at_last_line && line_display->insert_index == byte_offset + line->length)))
+           {
+             GdkRectangle cursor_rect;
+             GdkGC *cursor_gc;
+
+             /* we draw text using base color on filled cursor rectangle of cursor color
+              * (normally white on black) */
+             cursor_gc = _gtk_widget_get_cursor_gc (text_renderer->widget);
+
+             cursor_rect.x = x + line_display->x_offset + line_display->block_cursor.x;
+             cursor_rect.y = selection_y;
+             cursor_rect.width = line_display->block_cursor.width;
+             cursor_rect.height = selection_height;
+
+             gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer), NULL);
+             gdk_gc_set_clip_rectangle (cursor_gc, &cursor_rect);
+
+              gdk_draw_rectangle (text_renderer->drawable,
+                                  cursor_gc,
+                                  TRUE,
+                                  cursor_rect.x,
+                                  cursor_rect.y,
+                                  cursor_rect.width,
+                                  cursor_rect.height);
+
+             /* draw text under the cursor if any */
+             if (!line_display->cursor_at_line_end)
+               {
+                 GdkGC *cursor_text_gc;
+
+                 cursor_text_gc = text_renderer->widget->style->base_gc[text_renderer->widget->state];
+                 gdk_gc_set_clip_rectangle (cursor_text_gc, &cursor_rect);
+                 gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer), cursor_text_gc);
+                 text_renderer_set_state (text_renderer, CURSOR);
+
+                 pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer),
+                                                  line,
+                                                  PANGO_SCALE * x + line_rect.x,
+                                                  PANGO_SCALE * y + baseline);
+
+                 gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer), fg_gc);
+                 gdk_gc_set_clip_region (cursor_text_gc, NULL);
+               }
+
+              gdk_gc_set_clip_region (cursor_gc, NULL);
+           }
         }
 
       byte_offset += line->length;
index 2cb2981f7e4f398182f7e346e1aeaeae21867311..8faefef552661ee123d098539e71e41aaefa3106 100644 (file)
@@ -82,6 +82,7 @@
 #include "gtktextlayout.h"
 #include "gtktextbtree.h"
 #include "gtktextiterprivate.h"
+#include "gtktextutil.h"
 #include "gtkintl.h"
 #include "gtkalias.h"
 
@@ -397,6 +398,25 @@ gtk_text_layout_set_contexts (GtkTextLayout *layout,
   gtk_text_layout_invalidate_all (layout);
 }
 
+/**
+ * gtk_text_layout_set_overwrite_mode:
+ * @layout: a #GtkTextLayout
+ * @overwrite: overwrite mode
+ *
+ * Sets overwrite mode
+ **/
+void
+gtk_text_layout_set_overwrite_mode (GtkTextLayout *layout,
+                                   gboolean       overwrite)
+{
+  overwrite = overwrite != 0;
+  if (overwrite != layout->overwrite_mode)
+    {
+      layout->overwrite_mode = overwrite;
+      gtk_text_layout_invalidate_cursor_line (layout, TRUE);
+    }
+}
+
 /**
  * gtk_text_layout_set_cursor_direction:
  * @direction: the new direction(s) for which to draw cursors.
@@ -795,6 +815,7 @@ gtk_text_layout_invalidate_cache (GtkTextLayout *layout,
          g_slist_free (display->cursors);
          display->cursors = NULL;
          display->cursors_invalid = TRUE;
+         display->has_block_cursor = FALSE;
        }
       else
        {
@@ -1648,6 +1669,52 @@ add_child_attrs (GtkTextLayout      *layout,
   pango_attr_list_insert (attrs, attr);
 }
 
+/**
+ * get_block_cursor:
+ * @layout: a #GtkTextLayout
+ * @display: a #GtkTextLineDisplay
+ * @insert_iter: iter pointing to the cursor location
+ * @insert_index: cursor offset in the @display's layout, it may
+ * be different from @insert_iter's offset in case when preedit
+ * string is present.
+ * @pos: location to store cursor position
+ * @cursor_at_line_end: whether cursor is at the end of line
+ *
+ * Checks whether layout should display block cursor at given position.
+ * For this layout must be in overwrite mode and text at @insert_iter 
+ * must be editable.
+ **/
+static gboolean
+get_block_cursor (GtkTextLayout      *layout,
+                 GtkTextLineDisplay *display,
+                 const GtkTextIter  *insert_iter,
+                 gint                insert_index,
+                 GdkRectangle       *pos,
+                 gboolean           *cursor_at_line_end)
+{
+  PangoRectangle pango_pos;
+
+  if (layout->overwrite_mode &&
+      gtk_text_iter_editable (insert_iter, TRUE) &&
+      _gtk_text_util_get_block_cursor_location (display->layout,
+                                               insert_index,
+                                               &pango_pos,
+                                               cursor_at_line_end))
+    {
+      if (pos)
+       {
+         pos->x = PANGO_PIXELS (pango_pos.x);
+         pos->y = PANGO_PIXELS (pango_pos.y);
+         pos->width = PANGO_PIXELS (pango_pos.width);
+         pos->height = PANGO_PIXELS (pango_pos.height);
+       }
+
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
 static void
 add_cursor (GtkTextLayout      *layout,
             GtkTextLineDisplay *display,
@@ -1668,6 +1735,26 @@ add_cursor (GtkTextLayout      *layout,
        gtk_text_buffer_get_selection_bounds (layout->buffer, NULL, NULL)))
     return;
 
+  if (layout->overwrite_mode &&
+      _gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
+                                     seg->body.mark.obj))
+    {
+      GtkTextIter iter;
+      gboolean cursor_at_line_end;
+
+      _gtk_text_btree_get_iter_at_mark (_gtk_text_buffer_get_btree (layout->buffer),
+                                       &iter, seg->body.mark.obj);
+
+      if (get_block_cursor (layout, display, &iter, start,
+                           &display->block_cursor,
+                           &cursor_at_line_end))
+       {
+         display->has_block_cursor = TRUE;
+         display->cursor_at_line_end = cursor_at_line_end;
+         return;
+       }
+    }
+
   pango_layout_get_cursor_pos (display->layout, start, &strong_pos, &weak_pos);
 
   if (layout->cursor_direction == GTK_TEXT_DIR_NONE)
@@ -2139,6 +2226,9 @@ gtk_text_layout_get_line_display (GtkTextLayout *layout,
                            {
                              cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets, GINT_TO_POINTER (layout_byte_offset));
                              cursor_segs = g_slist_prepend (cursor_segs, seg);
+                             if (_gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
+                                                                 seg->body.mark.obj))
+                               display->insert_index = layout_byte_offset;
                            }
                         }
                      else
@@ -2612,6 +2702,66 @@ gtk_text_layout_get_cursor_locations (GtkTextLayout  *layout,
   gtk_text_layout_free_line_display (layout, display);
 }
 
+/**
+ * _gtk_text_layout_get_block_cursor:
+ * @layout: a #GtkTextLayout
+ * @pos: a #GdkRectangle to store block cursor position
+ *
+ * If layout is to display a block cursor, calculates its position
+ * and returns %TRUE. Otherwise it returns %FALSE. In case when
+ * cursor is visible, it simply returns the position stored in
+ * the line display, otherwise it has to compute the position
+ * (see get_block_cursor()).
+ **/
+gboolean
+_gtk_text_layout_get_block_cursor (GtkTextLayout *layout,
+                                  GdkRectangle  *pos)
+{
+  GtkTextLine *line;
+  GtkTextLineDisplay *display;
+  GtkTextIter iter;
+  GdkRectangle rect;
+  gboolean block = FALSE;
+
+  g_return_val_if_fail (layout != NULL, FALSE);
+
+  gtk_text_buffer_get_iter_at_mark (layout->buffer, &iter,
+                                    gtk_text_buffer_get_insert (layout->buffer));
+  line = _gtk_text_iter_get_text_line (&iter);
+  display = gtk_text_layout_get_line_display (layout, line, FALSE);
+
+  if (display->has_block_cursor)
+    {
+      block = TRUE;
+      rect = display->block_cursor;
+    }
+  else
+    {
+      gint index = display->insert_index;
+
+      if (index < 0)
+        index = gtk_text_iter_get_line_index (&iter);
+
+      if (get_block_cursor (layout, display, &iter, index, &rect, NULL))
+       block = TRUE;
+    }
+
+  if (block && pos)
+    {
+      gint line_top;
+
+      line_top = _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
+                                               line, layout);
+
+      *pos = rect;
+      pos->x += display->x_offset;
+      pos->y += line_top + display->top_margin;
+    }
+
+  gtk_text_layout_free_line_display (layout, display);
+  return block;
+}
+
 /**
  * gtk_text_layout_get_line_yrange:
  * @layout: a #GtkTextLayout
index ffc889f7113e769b8e945b80bd9f39455ca95ceb..cb35786454ab9ba551919be330a40c6676b1fa97 100644 (file)
@@ -174,6 +174,8 @@ struct _GtkTextLayout
   PangoAttrList *preedit_attrs;
   gint preedit_len;
   gint preedit_cursor;
+
+  guint overwrite_mode : 1;
 };
 
 struct _GtkTextLayoutClass
@@ -258,7 +260,10 @@ struct _GtkTextLineDisplay
   
   GdkColor *pg_bg_color;
 
+  GdkRectangle block_cursor;
   guint cursors_invalid : 1;
+  guint has_block_cursor : 1;
+  guint cursor_at_line_end : 1;
 };
 
 extern PangoAttrType gtk_text_attr_appearance_type;
@@ -276,6 +281,8 @@ void               gtk_text_layout_set_contexts          (GtkTextLayout     *lay
                                                          PangoContext      *rtl_context);
 void               gtk_text_layout_set_cursor_direction  (GtkTextLayout     *layout,
                                                           GtkTextDirection   direction);
+void              gtk_text_layout_set_overwrite_mode    (GtkTextLayout     *layout,
+                                                         gboolean           overwrite);
 void               gtk_text_layout_set_keyboard_direction (GtkTextLayout     *layout,
                                                           GtkTextDirection keyboard_dir);
 void               gtk_text_layout_default_style_changed (GtkTextLayout     *layout);
@@ -380,6 +387,8 @@ void     gtk_text_layout_get_cursor_locations (GtkTextLayout     *layout,
                                                GtkTextIter       *iter,
                                                GdkRectangle      *strong_pos,
                                                GdkRectangle      *weak_pos);
+gboolean _gtk_text_layout_get_block_cursor    (GtkTextLayout     *layout,
+                                              GdkRectangle      *pos);
 gboolean gtk_text_layout_clamp_iter_to_vrange (GtkTextLayout     *layout,
                                                GtkTextIter       *iter,
                                                gint               top,
index 4c6f16977a4c7f586774a910b03634a41797d918..f601e2f9bd9206ab52080734173fe338026b1dd4 100644 (file)
@@ -362,3 +362,146 @@ _gtk_text_util_create_rich_drag_icon (GtkWidget     *widget,
 
    return drawable;
 }
+
+
+static gint
+layout_get_char_width (PangoLayout *layout)
+{
+  gint width;
+  PangoFontMetrics *metrics;
+  const PangoFontDescription *font_desc;
+  PangoContext *context = pango_layout_get_context (layout);
+
+  font_desc = pango_layout_get_font_description (layout);
+  if (!font_desc)
+    font_desc = pango_context_get_font_description (context);
+
+  metrics = pango_context_get_metrics (context, font_desc, NULL);
+  width = pango_font_metrics_get_approximate_char_width (metrics);
+  pango_font_metrics_unref (metrics);
+
+  return width;
+}
+
+/**
+ * _gtk_text_util_get_block_cursor_location
+ * @layout: a #PangoLayout
+ * @index: index at which cursor is located
+ * @rect: cursor location
+ *
+ * Returns: whether cursor should actually be drawn as a rectangle.
+ * It may not be the case if character at index is invisible.
+ **/
+gboolean
+_gtk_text_util_get_block_cursor_location (PangoLayout    *layout,
+                                         gint            index,
+                                         PangoRectangle *pos,
+                                         gboolean       *at_line_end)
+{
+  PangoRectangle strong_pos, weak_pos;
+  PangoLayoutLine *layout_line;
+  gboolean rtl;
+  gint line_no;
+
+  g_return_val_if_fail (layout != NULL, FALSE);
+  g_return_val_if_fail (index >= 0, FALSE);
+  g_return_val_if_fail (pos != NULL, FALSE);
+
+  pango_layout_index_to_pos (layout, index, pos);
+
+  if (pos->width != 0)
+    {
+      /* cursor is at some visible character, good */
+      if (at_line_end)
+       *at_line_end = FALSE;
+      if (pos->width < 0)
+       {
+         pos->x += pos->width;
+         pos->width = -pos->width;
+       }
+      return TRUE;
+    }
+
+  pango_layout_index_to_line_x (layout, index, FALSE, &line_no, NULL);
+  g_return_val_if_fail (line_no >= 0, FALSE);
+  layout_line = pango_layout_get_line_readonly (layout, line_no);
+
+  /* end of layout, get last line */
+  if (!layout_line)
+    {
+      line_no -= 1;
+      layout_line = pango_layout_get_line_readonly (layout, line_no);
+    }
+
+  g_return_val_if_fail (layout_line != NULL, FALSE);
+
+  if (index < layout_line->start_index + layout_line->length)
+    {
+      /* cursor points to some zero-width character, do not
+       * bother with block cursor */
+      return FALSE;
+    }
+
+  /* Cursor is at the line end. It may be an empty line, or it could
+   * be on the left or on the right depending on text direction, or it
+   * even could be in the middle of visual layout in bidi text. */
+
+  pango_layout_get_cursor_pos (layout, index, &strong_pos, &weak_pos);
+
+  if (strong_pos.x != weak_pos.x)
+    {
+      /* do not show block cursor in this case, since the character typed
+       * in may or may not appear at the cursor position */
+      return FALSE;
+    }
+
+  /* In case when index points to the end of line, pos->x is always most right
+   * pixel of the layout line, so we need to correct it for RTL text. */
+  if (layout_line->length)
+    {
+      gint left, right;
+      const gchar *text;
+      const gchar *p;
+
+      text = pango_layout_get_text (layout);
+      p = g_utf8_prev_char (text + index);
+
+      pango_layout_line_index_to_x (layout_line, p - text, FALSE, &left);
+      pango_layout_line_index_to_x (layout_line, p - text, TRUE, &right);
+
+      if (MIN (left, right) <= 0)
+       {
+          /* last character is on the left, RTL */
+
+         PangoLayoutIter *iter;
+         PangoRectangle line_rect;
+         gint i;
+
+         iter = pango_layout_get_iter (layout);
+         for (i = 0; i < line_no; i++)
+           pango_layout_iter_next_line (iter);
+         pango_layout_iter_get_line_extents (iter, NULL, &line_rect);
+         pango_layout_iter_free (iter);
+
+          rtl = TRUE;
+         pos->x = MIN (left, right) + line_rect.x;
+       }
+      else
+       rtl = FALSE;
+    }
+  else
+    {
+      PangoContext *context = pango_layout_get_context (layout);
+      rtl = pango_context_get_base_dir (context) == PANGO_DIRECTION_RTL;
+    }
+
+  pos->width = layout_get_char_width (layout);
+
+  if (rtl)
+    pos->x -= pos->width - 1;
+
+  if (at_line_end)
+    *at_line_end = TRUE;
+
+  return pos->width != 0;
+}
index e69a925af1ad095d9a24f383dac35bcd15cbea44..5b3070b2127800ea9c83c7ef785453f363dcd466 100644 (file)
@@ -45,9 +45,6 @@ void _gtk_text_util_append_special_char_menuitems (GtkMenuShell              *me
                                                    GtkTextUtilCharChosenFunc  func,
                                                    gpointer                   data);
 
-
-G_END_DECLS
-
 GdkPixmap* _gtk_text_util_create_drag_icon      (GtkWidget     *widget,
                                                  gchar         *text,
                                                  gsize          len);
@@ -56,4 +53,11 @@ GdkPixmap* _gtk_text_util_create_rich_drag_icon (GtkWidget     *widget,
                                                  GtkTextIter   *start,
                                                  GtkTextIter   *end);
 
+gboolean _gtk_text_util_get_block_cursor_location (PangoLayout    *layout,
+                                                  gint            index_,
+                                                  PangoRectangle *rectangle,
+                                                  gboolean       *at_line_end);
+
+G_END_DECLS
+
 #endif /* __GTK_TEXT_UTIL_H__ */
index f37a04de8108d2d6bc0f7b0d2c55c9c1e28d049a..17ec3172bf731bd4f1774a329c4d9e2ca0fe6cf8 100644 (file)
@@ -2054,6 +2054,8 @@ gtk_text_view_set_editable (GtkTextView *text_view,
 
       if (text_view->layout)
         {
+         gtk_text_layout_set_overwrite_mode (text_view->layout,
+                                             text_view->overwrite_mode && text_view->editable);
           text_view->layout->default_style->editable = text_view->editable;
           gtk_text_layout_default_style_changed (text_view->layout);
         }
@@ -5422,7 +5424,20 @@ gtk_text_view_paste_clipboard (GtkTextView *text_view)
 static void
 gtk_text_view_toggle_overwrite (GtkTextView *text_view)
 {
+  if (text_view->text_window)
+    text_window_invalidate_cursors (text_view->text_window);
+
   text_view->overwrite_mode = !text_view->overwrite_mode;
+
+  if (text_view->layout)
+    gtk_text_layout_set_overwrite_mode (text_view->layout,
+                                       text_view->overwrite_mode && text_view->editable);
+
+  if (text_view->text_window)
+    text_window_invalidate_cursors (text_view->text_window);
+
+  gtk_text_view_pend_cursor_blink (text_view);
+
   g_object_notify (G_OBJECT (text_view), "overwrite");
 }
 
@@ -5461,11 +5476,7 @@ gtk_text_view_set_overwrite (GtkTextView *text_view,
   overwrite = overwrite != FALSE;
 
   if (text_view->overwrite_mode != overwrite)
-    {
-      text_view->overwrite_mode = overwrite;
-
-      g_object_notify (G_OBJECT (text_view), "overwrite");
-    }
+    gtk_text_view_toggle_overwrite (text_view);
 }
 
 /**
@@ -6040,6 +6051,9 @@ gtk_text_view_ensure_layout (GtkTextView *text_view)
       else
         gtk_text_layout_set_cursor_visible (text_view->layout, FALSE);
 
+      gtk_text_layout_set_overwrite_mode (text_view->layout,
+                                         text_view->overwrite_mode && text_view->editable);
+
       ltr_context = gtk_widget_create_pango_context (GTK_WIDGET (text_view));
       pango_context_set_base_dir (ltr_context, PANGO_DIRECTION_LTR);
       rtl_context = gtk_widget_create_pango_context (GTK_WIDGET (text_view));
@@ -7764,6 +7778,12 @@ text_window_invalidate_cursors (GtkTextWindow *win)
   gtk_text_buffer_get_iter_at_mark (text_view->buffer, &iter,
                                     gtk_text_buffer_get_insert (text_view->buffer));
 
+  if (_gtk_text_layout_get_block_cursor (text_view->layout, &strong))
+    {
+      text_window_invalidate_rect (win, &strong);
+      return;
+    }
+
   gtk_text_layout_get_cursor_locations (text_view->layout, &iter,
                                         &strong, &weak);